package btcbot.communication;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import btcbot.entities.Account;
import btcbot.entities.Depth;
import btcbot.util.PropertyManager;

/**
 * TODO Comment
 *
 * @author mollagnier
 */
@SuppressWarnings("restriction")
public class CommunicationManager {
	private static final Logger logger = Logger.getLogger(CommunicationManager.class.getName());

	// Standard MtGox date pattern
    public static final String DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";

	// Authentification credentials properties keys
    private static final String KEY = "communication.authentification.key";
    private static final String SECRET = "communication.authentification.secret";

    // Basic communication properties keys
	private static final String TIMEOUT = "communication.timeout";
	private static final String URL_ROOT = "communication.urlRoot";

	// MtGox WS URI list
    private static final String URI_INFO = "generic/info";
    private static final String URI_DEPTH = "BTCEUR/depth/fetch";

    /**
     * TODO Comment
     *
     * @return
     * @throws Exception
     */
    public static Account getAccount() throws Exception {
        JSONObject jsonAccount = CommunicationManager.getJSON(URI_INFO, new HashMap<String, String>());
        return new Account(jsonAccount);
    }

    /**
     * TODO Comment
     *
     * @return
     * @throws Exception
     */
    public static Depth getDepth() throws Exception {
        JSONObject jsonDepth = CommunicationManager.getJSON(URI_DEPTH, new HashMap<String, String>());
        return new Depth(jsonDepth);
    }

	/**
	 * TODO Comment
	 *
	 * @param uri
	 * @param params
	 * @return
	 * @throws Exception
	 */
	private static JSONObject getJSON(String uri, HashMap<String, String> params) throws Exception {
		HttpURLConnection connection = null;
		BufferedReader rd = null;
		StringBuilder sb = null;
		String line = null;
		JSONObject jsonResult = null;
		try {
			// Adds nonce and builds params
			params.put("nonce", String.valueOf(System.currentTimeMillis()));
			String paramsString = CommunicationManager.buildParams(params);

			// Sets up the connection
			connection = (HttpURLConnection) new URL(PropertyManager.get(URL_ROOT) + uri).openConnection();
			connection.setRequestMethod("GET");
			connection.setDoOutput(true);
			connection.setReadTimeout(PropertyManager.getInteger(TIMEOUT));
			connection.setRequestProperty("Rest-Key", PropertyManager.get(KEY));
			connection.setRequestProperty("Rest-Sign", CommunicationManager.getSignature(paramsString));
			connection.connect();
			connection.getOutputStream().write(paramsString.getBytes());

			// Reads the result from the server
			rd = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			sb = new StringBuilder();
			while ((line = rd.readLine()) != null) {
				sb.append(line);
			}
			jsonResult = (JSONObject) JSONSerializer.toJSON(sb.toString());

		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (ProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			connection.disconnect();
			rd = null;
			sb = null;
			connection = null;
		}

		if (!jsonResult.getString("result").equals("success")) {
			throw new Exception("Communication error");
		}

		return jsonResult.getJSONObject("return");
	}

	/**
	 * TODO Comment
	 *
	 * @param params
	 * @return
	 */
	private static String buildParams(HashMap<String, String> params) {
		String result = "";
		for (String key : params.keySet()) {
			if (result.length() > 0)
				result += '&';
			try {
				result += URLEncoder.encode(key, "UTF-8") + "=" + URLEncoder.encode(params.get(key), "UTF-8");
			} catch (Exception e) {
				logger.log(Level.SEVERE, null, e);
			}
		}
		return result;
	}

	/**
	 * TODO Comment
	 *
	 * @param paramsString
	 * @return
	 */
	private static String getSignature(String paramsString) {
		String signature = null;
		try {
			Mac mac = Mac.getInstance("HmacSHA512");
			mac.init(new SecretKeySpec((new BASE64Decoder()).decodeBuffer(PropertyManager.get(SECRET)), "HmacSHA512"));
			signature = (new BASE64Encoder()).encode(mac.doFinal(paramsString.getBytes()));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return signature.replaceAll("\n", "").replaceAll("\r", "");
	}

}
